// Copyright 2022 The kubegems.io Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package apis

import (
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/go-resty/resty/v2"
	"github.com/kiali/kiali/jaeger"
	jaegerModels "github.com/kiali/kiali/jaeger/model/json"
	"kubegems.io/kubegems/pkg/log"
)

// Generated by https://quicktype.io

func NewJaegerHandler(server string) *jaegerHandler {
	return &jaegerHandler{Server: server}
}

type jaegerHandler struct {
	Server string
}

//	@Tags			Agent.V1
//	@Summary		Jaeger span count
//	@Description	Jaeger span count
//	@Accept			json
//	@Produce		json
//	@Param			namespace	query		string											true	"workload namespace"
//	@Param			name		query		string											true	"workload name"
//	@Param			app			query		string											true	"workload app label value"
//	@Success		200			{object}	handlers.ResponseStruct{Data=map[string]int}	"span数"
//	@Router			/v1/proxy/cluster/{cluster}/custom/jaeger/v1/span [get]
//	@Security		JWT
func (p *jaegerHandler) GetSpanCount(c *gin.Context) {
	// 参考kiali实现:
	// 1. 获取service链路
	// 2. 过滤workload链路
	// 3. 取最近的span数
	workloadName := c.Query("name")
	workloadNamespace := c.Query("namespace")
	appName := c.Query("app") // 也就是jaeger中的service名

	now := time.Now()
	traceResp := jaeger.JaegerResponse{}
	req := resty.New().R().
		SetQueryParam("start", strconv.Itoa(int(now.Add(-1*time.Hour).UnixMicro()))).
		SetQueryParam("end", strconv.Itoa(int(now.UnixMicro()))).
		SetQueryParam("limit", "100").
		SetQueryParam("service", fmt.Sprintf("%s.%s", appName, workloadNamespace)).
		SetResult(&traceResp)

	resp, err := req.Get(p.Server + "/api/traces")
	if err != nil {
		NotOK(c, err)
		return
	}
	log.Info("trace", "url", resp.Request.URL)

	traces := []jaegerModels.Trace{}
	for _, trace := range traceResp.Data {
		if matchesWorkload(&trace, workloadNamespace, workloadName) {
			traces = append(traces, trace)
		}
	}

	count := 0
	if len(traces) > 0 {
		count = len(traces[len(traces)-1].Spans)
	}
	OK(c, gin.H{
		"count": count,
	})
}

// copy from kiali
func matchesWorkload(trace *jaegerModels.Trace, namespace, workload string) bool {
	for _, span := range trace.Spans {
		if process, ok := trace.Processes[span.ProcessID]; ok {
			span.Process = &process
		}
		if spanMatchesWorkload(&span, namespace, workload) {
			return true
		}
	}
	return false
}

func spanMatchesWorkload(span *jaegerModels.Span, namespace, workload string) bool {
	// For envoy traces, with a workload named "ai-locals", node_id is like:
	// sidecar~172.17.0.20~ai-locals-6d8996bff-ztg6z.default~default.svc.cluster.local
	for _, tag := range span.Tags {
		if tag.Key == "node_id" {
			if v, ok := tag.Value.(string); ok {
				parts := strings.Split(v, "~")
				if len(parts) >= 3 && strings.HasPrefix(parts[2], workload) && strings.HasSuffix(parts[2], namespace) {
					return true
				}
			}
		}
	}
	// Tag not found => try with 'hostname' in process' tags
	if span.Process != nil {
		for _, tag := range span.Process.Tags {
			if tag.Key == "hostname" {
				if v, ok := tag.Value.(string); ok {
					if strings.HasPrefix(v, workload) {
						return true
					}
				}
			}
		}
	}
	return false
}
